home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 …ember: Reference Library / Dev.CD Dec 96 RL / Dev.CD Dec 96 RL.toast / Technical Documentation / Macintosh Technical Notes / technotes / ic / launchwithdoc2.hqx / LaunchWithDoc2 / LaunchWithDoc2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-28  |  13.8 KB  |  457 lines

  1. /*
  2.  *   LaunchWithDoc v 2
  3.  *
  4.  *   Greg Robbins  August 1993
  5.  *
  6.  *   Document-launching sample program
  7.  *   Loosely based on C.K. Haun's LaunchWithDoc
  8.  *
  9.  *   This snippet includes these useful routines:
  10.  *
  11.  *      OpenSpecifiedDocument
  12.  *        finds the creator application for a document, whether or not
  13.  *        the app is running, launches the app if necessary, and sends
  14.  *        the Apple Event necessary to get the app to open the document
  15.  *
  16.  *      FindApplicationFromDocument
  17.  *        searches the mounted volumes for the application which
  18.  *        created a document
  19.  *
  20.  *      LaunchApplicationWithDocument
  21.  *        launches an application which is not running and passes it
  22.  *        the OpenDocuments event as part of the launch parameters
  23.  *
  24.  *      SendOpenDocumentEventToProcess
  25.  *        sends an OpenDocuments Apple event to a running program
  26.  *
  27.  *  Remember that a target application need not be Apple event aware
  28.  *  in order for the OpenDocuments event to succeed (the System will
  29.  *  pull "puppet strings", simulating the events necessary to make the
  30.  *  target app open the document)
  31.  *
  32.  *  However, LaunchWithDoc must be high level event aware (as set in the
  33.  *  SIZE resource) in order to send Apple events using AESend
  34.  *
  35.  */
  36.  
  37. #include <QuickDraw.h>
  38. #include <StandardFile.h>
  39. #include <Fonts.h>
  40. #include <Menus.h>
  41. #include <Dialogs.h>
  42. #include <Events.h>
  43. #include <TextEdit.h>
  44. #include <Errors.h>
  45. #include <Processes.h>
  46. #include <AppleEvents.h>
  47. #include <Aliases.h>
  48.  
  49.  
  50. // prototypes
  51.  
  52. OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr);
  53. OSErr LaunchApplicationWithDocument(const FSSpec * applicationFSSpecPtr,
  54.     const FSSpec * documentFSSpecPtr);
  55. OSErr SendOpenDocumentEventToProcess(ProcessSerialNumber *targetPSN,
  56.     const FSSpec * documentFSSpecPtr);
  57. OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr,
  58.     FSSpecPtr applicationFSSpecPtr);
  59. void ReportError(StringPtr messageStr);
  60.  
  61.  
  62. // main program
  63. //
  64. // the main routine raises a std file dialog to let the
  65. // user choose a document and then opens the document
  66. // in the appropriate application
  67.  
  68. void main(void)
  69. {
  70.     OSErr retCode;
  71.     StandardFileReply documentStdFileReply;
  72.     SFTypeList mySFTypeList;
  73.     
  74.     // initialize the toolbox
  75.     InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus();
  76.     TEInit(); InitDialogs(nil); InitCursor();
  77.     
  78.     // Simplest case: get a document and open it
  79.     
  80.     StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
  81.     if (documentStdFileReply.sfGood) {
  82.         
  83.         retCode = OpenSpecifiedDocument(&documentStdFileReply.sfFile);
  84.         if (retCode != noErr) ReportError("\p OpenDocument failed");
  85.     }
  86. }
  87.  
  88. void ReportError(StringPtr messageStr)
  89. {
  90.     DebugStr(messageStr);
  91. }
  92.  
  93.  
  94. // OpenSpecifiedDocument searches to see if the application which
  95. // created the document is already running.  If so, it sends
  96. // an OpenSpecifiedDocuments Apple event to the target application
  97. // (remember that, because of puppet strings, this works even
  98. // if the target application is not Apple event-aware.)
  99.  
  100. OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr)
  101. {
  102.     OSErr retCode;
  103.     ProcessSerialNumber currPSN;
  104.     ProcessInfoRec currProcessInfo;
  105.     FSSpec applicationSpec;
  106.     FInfo documentFInfo;
  107.     Boolean foundRunningProcessFlag;
  108.     
  109.     // verify the document file exists and get its creator type
  110.     
  111.     retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo);
  112.     if (retCode != noErr) goto Bail;
  113.     
  114.     // check the current processes to see if the creator app is already
  115.     // running, and get its process serial number (as currPSN)
  116.     
  117.     currPSN.lowLongOfPSN = kNoProcess;
  118.     currPSN.highLongOfPSN = 0;
  119.     
  120.     currProcessInfo.processInfoLength = sizeof(ProcessInfoRec);
  121.     currProcessInfo.processName = nil;
  122.     currProcessInfo.processAppSpec = &applicationSpec;
  123.     
  124.     foundRunningProcessFlag = false;
  125.     while (GetNextProcess(&currPSN) == noErr) {
  126.         if (GetProcessInformation(&currPSN, &currProcessInfo) == noErr) {
  127.             if (currProcessInfo.processSignature == documentFInfo.fdCreator) {
  128.                 foundRunningProcessFlag = true;
  129.                 break;
  130.             }
  131.         }
  132.     }
  133.     
  134.     // if the creator is running, send it an OpenDocuments Apple event
  135.     // since there is no need to launch it
  136.     
  137.     if (foundRunningProcessFlag)
  138.         retCode = SendOpenDocumentEventToProcess(&currPSN, documentFSSpecPtr);
  139.     
  140.     // else if the creator is not running, find it on disk and launch
  141.     // it with the OpenDocuments event included as a part of the
  142.     // launch parameters
  143.     
  144.     else {
  145.         retCode = FindApplicationFromDocument(documentFSSpecPtr, &applicationSpec);
  146.         
  147.         if (retCode == noErr)
  148.         
  149.             retCode = LaunchApplicationWithDocument(&applicationSpec,
  150.                 documentFSSpecPtr);
  151.     }
  152.     
  153. Bail:
  154.     return retCode;
  155. }
  156.  
  157.  
  158. // given an application and a document, LaunchApplicationWithDocument
  159. // launches the application and passes the application an
  160. // OpenDocuments event for the document
  161.  
  162. OSErr LaunchApplicationWithDocument(const FSSpec * applicationFSSpecPtr,
  163.     const FSSpec * documentFSSpecPtr)
  164. {
  165.     OSErr retCode;
  166.     LaunchParamBlockRec launchParams;
  167.     ProcessSerialNumber myPSN;
  168.     AppleEvent theAppleEvent;
  169.     AEDesc myAddrDesc, launchParamDesc, docDesc;
  170.     AEDescList docDescList;
  171.     AliasHandle docAlias;
  172.     
  173.     // to simplify cleanup, ensure that handles are nil to start
  174.     theAppleEvent.dataHandle = nil;
  175.     launchParamDesc.dataHandle = nil;
  176.     docDescList.dataHandle = nil;
  177.     docDesc.dataHandle = nil;
  178.     docAlias = nil;
  179.     
  180.     // the Apple event will need a valid address descriptor (even though its
  181.     // contents will not matter since we will not be calling AESend) so make 
  182.     // one using my own serial number
  183.     
  184.     (void) GetCurrentProcess(&myPSN);
  185.     retCode = AECreateDesc(typeProcessSerialNumber, (Ptr) &myPSN,
  186.         sizeof(ProcessSerialNumber), &myAddrDesc);
  187.     if (retCode != noErr) goto Bail;
  188.     
  189.     // make a descriptor list containing just a descriptor with an
  190.     // alias to the document
  191.     
  192.     retCode = AECreateList(nil, 0, false, &docDescList);
  193.     if (retCode != noErr) goto Bail;
  194.     
  195.     retCode = NewAlias(nil, documentFSSpecPtr, &docAlias);
  196.     if (retCode != noErr) goto Bail;
  197.     
  198.     HLock((Handle) docAlias);
  199.     retCode = AECreateDesc(typeAlias, (Ptr) *docAlias, 
  200.         GetHandleSize((Handle) docAlias), &docDesc);
  201.     HUnlock((Handle) docAlias);
  202.     if (retCode != noErr) goto Bail;
  203.     
  204.     retCode = AEPutDesc(&docDescList, 0, &docDesc);
  205.     if (retCode != noErr) goto Bail;
  206.     
  207.     // now make the 'odoc' AppleEvent descriptor and insert the 
  208.     // document descriptor list as the direct object
  209.     
  210.     retCode = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
  211.         &myAddrDesc, kAutoGenerateReturnID, kAnyTransactionID,
  212.         &theAppleEvent);
  213.     if (retCode != noErr) goto Bail;
  214.     
  215.     retCode = AEPutParamDesc(&theAppleEvent, keyDirectObject, &docDescList);
  216.     if (retCode != noErr) goto Bail;
  217.     
  218.     // this Apple event will not be sent but rather will be used
  219.     // as a parameter to the LaunchApplication call, so coerce it
  220.     // to the magic type typeAppParamters
  221.     
  222.     retCode = AECoerceDesc(&theAppleEvent, typeAppParameters, &launchParamDesc);
  223.     if (retCode != noErr) goto Bail;
  224.     
  225.     // finally, fill in the launch parameter block, including the
  226.     // Apple event, and make the launch call
  227.     
  228.     HLock((Handle) launchParamDesc.dataHandle);
  229.     launchParams.launchAppParameters =
  230.         (AppParametersPtr) *(launchParamDesc.dataHandle);
  231.         
  232.     launchParams.launchBlockID = extendedBlock;
  233.     launchParams.launchEPBLength = extendedBlockLen;
  234.     launchParams.launchFileFlags = launchNoFileFlags;
  235.     launchParams.launchControlFlags = launchContinue;
  236.     launchParams.launchAppSpec = (FSSpecPtr) applicationFSSpecPtr;
  237.  
  238.     retCode = LaunchApplication(&launchParams);
  239.     
  240. Bail:
  241.     // dispose of everything that was allocated
  242.     
  243.     if (theAppleEvent.dataHandle != nil)     (void) AEDisposeDesc(&theAppleEvent);
  244.     if (launchParamDesc.dataHandle != nil)   (void) AEDisposeDesc(&launchParamDesc);
  245.     if (docDescList.dataHandle != nil)       (void) AEDisposeDesc(&docDescList);
  246.     if (docDesc.dataHandle != nil)           (void) AEDisposeDesc(&docDesc);
  247.     if (launchParamDesc.dataHandle != nil)   (void) AEDisposeDesc(&launchParamDesc);
  248.     if (docAlias != nil)
  249.         DisposeHandle((Handle) docAlias);
  250.     
  251.     return retCode;
  252.  
  253. }
  254.  
  255.  
  256.  
  257. // given an application's serial number and a document, 
  258. // SendOpenDocumentEventToProcess passes 
  259. // the application an OpenDocuments event for the document
  260.  
  261. OSErr SendOpenDocumentEventToProcess(ProcessSerialNumber *targetPSN,
  262.     const FSSpec * documentFSSpecPtr)
  263. {
  264.     OSErr retCode;
  265.     AppleEvent theAppleEvent, theReplyEvent;
  266.     AEDesc targetAddrDesc, docDesc;
  267.     AEDescList docDescList;
  268.     AliasHandle docAlias;
  269.     
  270.     // to simplify cleanup, ensure that handles are nil to start
  271.     theAppleEvent.dataHandle = nil;
  272.     docDescList.dataHandle = nil;
  273.     docDesc.dataHandle = nil;
  274.     docAlias = nil;
  275.     
  276.     // create an address descriptor based on the serial number of
  277.     // the target process
  278.     
  279.     retCode = AECreateDesc(typeProcessSerialNumber, (Ptr) targetPSN,
  280.         sizeof(ProcessSerialNumber), &targetAddrDesc);
  281.     if (retCode != noErr) goto Bail;
  282.     
  283.     // make a descriptor list containing just a descriptor with an
  284.     // alias to the document
  285.     
  286.     retCode = AECreateList(nil, 0, false, &docDescList);
  287.     if (retCode != noErr) goto Bail;
  288.     
  289.     retCode = NewAlias(nil, documentFSSpecPtr, &docAlias);
  290.     if (retCode != noErr) goto Bail;
  291.     
  292.     HLock((Handle) docAlias);
  293.     retCode = AECreateDesc(typeAlias, (Ptr) *docAlias, 
  294.         GetHandleSize((Handle) docAlias), &docDesc);
  295.     HUnlock((Handle) docAlias);
  296.     if (retCode != noErr) goto Bail;
  297.     
  298.     retCode = AEPutDesc(&docDescList, 0, &docDesc);
  299.     if (retCode != noErr) goto Bail;
  300.     
  301.     // now make the 'odoc' AppleEvent descriptor and insert the 
  302.     // document descriptor list as the direct object
  303.     
  304.     retCode = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
  305.         &targetAddrDesc, kAutoGenerateReturnID, kAnyTransactionID,
  306.         &theAppleEvent);
  307.     if (retCode != noErr) goto Bail;
  308.     
  309.     retCode = AEPutParamDesc(&theAppleEvent, keyDirectObject, &docDescList);
  310.     if (retCode != noErr) goto Bail;
  311.     
  312.     // finally, send the Apple event
  313.     retCode = AESend(&theAppleEvent, &theReplyEvent, kAENoReply, 
  314.         kAENormalPriority, kAEDefaultTimeout, nil, nil);
  315.     
  316. Bail:
  317.     // dispose of everything that was allocated
  318.     
  319.     if (theAppleEvent.dataHandle != nil)  (void) AEDisposeDesc(&theAppleEvent);
  320.     if (docDescList.dataHandle != nil)  (void) AEDisposeDesc(&docDescList);
  321.     if (docDesc.dataHandle != nil)  (void) AEDisposeDesc(&docDesc);
  322.     if (docAlias != nil)  DisposeHandle((Handle) docAlias);
  323.     
  324.     return retCode;
  325.  
  326. }
  327.  
  328.  
  329. // FindApplicationFromDocument uses the Desktop Database to
  330. // locate the creator application for the given document
  331. //
  332. // this routine will first check the desktop database of the disk
  333. // containing the document, then the desktop database of all local
  334. // disks, then the desktop databases of all server volumes
  335. // (so up to three passes will be made)
  336.  
  337. OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr,
  338.     FSSpecPtr applicationFSSpecPtr)
  339. {
  340.     enum { documentPass, localPass, remotePass, donePass } volumePass;
  341.     DTPBRec desktopParams;
  342.     HParamBlockRec hfsParams;
  343.     FInfo documentFInfo;
  344.     short volumeIndex;
  345.     Boolean foundFlag;
  346.     GetVolParmsInfoBuffer volumeInfoBuffer;
  347.     OSErr retCode;
  348.     
  349.     // verify the document file exists and get its creator type
  350.     
  351.     retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo);
  352.     if (retCode != noErr) goto Bail;
  353.     
  354.     volumePass = documentPass;
  355.     volumeIndex = 0;
  356.     
  357.     do {
  358.         
  359.         // first, find the vRefNum of the volume whose Desktop Database
  360.         // we're checking this time
  361.         
  362.         // if we're on the initial pass (documentPass) just use
  363.         // the vRefNum of the document itself
  364.         
  365.         if (volumePass == documentPass)
  366.         
  367.             desktopParams.ioVRefNum = documentFSSpecPtr->vRefNum;
  368.         
  369.         // otherwise, find the vRefNum of the next volume appropriate
  370.         // for this pass
  371.         
  372.         else {
  373.             
  374.             volumeIndex++;
  375.             
  376.             // convert the volumeIndex into a vRefNum
  377.             
  378.             hfsParams.volumeParam.ioNamePtr = nil;
  379.             hfsParams.volumeParam.ioVRefNum = 0;
  380.             hfsParams.volumeParam.ioVolIndex = volumeIndex;
  381.             retCode = PBHGetVInfoSync(&hfsParams);
  382.             
  383.             // a nsvErr indicates that the current pass is over
  384.             if (retCode == nsvErr) goto SkipThisVolume;
  385.             if (retCode != noErr) goto Bail;
  386.             
  387.             // since we handled the document volume during the documentPass,
  388.             // skip it if we have hit that volume again
  389.             
  390.             if (hfsParams.volumeParam.ioVRefNum == documentFSSpecPtr->vRefNum)
  391.                 goto SkipThisVolume;
  392.             
  393.             // call GetVolParms to determine if this volume is a server
  394.             // (a remote volume)
  395.             
  396.             hfsParams.ioParam.ioBuffer = (Ptr) &volumeInfoBuffer;
  397.             hfsParams.ioParam.ioReqCount = sizeof(GetVolParmsInfoBuffer);
  398.             retCode = PBHGetVolParmsSync(&hfsParams);
  399.             if (retCode != noErr) goto Bail;
  400.             
  401.             // if the vMServerAdr field of the volume information buffer
  402.             // is zero, this is a local volume; skip this volume
  403.             // if it's local on a remote pass or remote on a local pass
  404.             
  405.             if ((volumeInfoBuffer.vMServerAdr != 0) !=
  406.                 (volumePass == remotePass)) goto SkipThisVolume;
  407.             
  408.             // okay, now we've found the vRefNum for our desktop database call
  409.             
  410.             desktopParams.ioVRefNum = hfsParams.volumeParam.ioVRefNum;
  411.         }
  412.         
  413.         // find the path refNum for the desktop database for
  414.         // the volume we're interested in
  415.         
  416.         desktopParams.ioNamePtr = nil;
  417.         
  418.         retCode = PBDTGetPath(&desktopParams);
  419.         if (retCode == noErr && desktopParams.ioDTRefNum != 0) {
  420.         
  421.             // use the GetAPPL call to find the preferred application
  422.             // for opening any document with this one's creator
  423.             
  424.             desktopParams.ioIndex = 0;
  425.             desktopParams.ioFileCreator = documentFInfo.fdCreator;
  426.             desktopParams.ioNamePtr = applicationFSSpecPtr->name;
  427.             retCode = PBDTGetAPPLSync(&desktopParams);
  428.             
  429.             if (retCode == noErr) {
  430.             
  431.                 // okay, found it; fill in the application file spec
  432.                 // and set the flag indicating we're done
  433.                 
  434.                 applicationFSSpecPtr->parID = desktopParams.ioAPPLParID;
  435.                 applicationFSSpecPtr->vRefNum = desktopParams.ioVRefNum;
  436.                 foundFlag = true;
  437.                 
  438.             }
  439.         }
  440.         
  441.     SkipThisVolume:
  442.     
  443.         // if retCode indicates a no such volume error or if this
  444.         // was the first pass, it's time to move on to the next pass
  445.         
  446.         if (retCode == nsvErr || volumePass == documentPass) {
  447.             volumePass++;
  448.             volumeIndex = 0;
  449.         }
  450.         
  451.     } while (foundFlag == false && volumePass != donePass);
  452.     
  453. Bail:
  454.     return retCode;
  455. }
  456.  
  457.